/* -*- c++ -*-
   Repr.h - Representation of language elements.
   Copyright (C) 2011 Matthew Wahab <mwb.cde@gmail.com>

   This file is part of GCC-LLVMIR.

   GCC-LLVMIR is free software; you can redistribute it and/or modify it under
   the terms of the GNU General Public License as published by the Free
   Software Foundation; either version 3, or (at your option) any later
   version.

   GCC-LLVMIR is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   for more details.

   You should have received a copy of the GNU General Public License
   along with GCC-LLVMIR; see the file COPYING3.  If not see
   <http://www.gnu.org/licenses/>.  */

/* This is a C++ file. */

#ifndef REPR_H
#define REPR_H

#include <map>
#include <vector>
#include <list>

#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/Type.h"
#include "llvm/DerivedTypes.h"
#include "llvm/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_os_ostream.h"
#include "llvm/Assembly/Parser.h"
#include "llvm/Assembly/PrintModulePass.h"

#include "llvmir-system.h"
#include "SimpleList.h"


/*
 * Representation of language elements
 */

namespace llvmir
{
  /* TrackerList: Track allocated objects, to properly free used
     memory.

     TrackerNode: Class to embed in objects to be tracked. The
     TrackerNode will properly remove itself from its TrackerList when
     it is deleted.
  */
  template <class ParentType>
    class Tracker: public SimpleList< Tracker<ParentType> >::Node
  {
  public:
    /* Fields */

    // Object in which this tracker is embedded.
    ParentType* const parent;

    /* Constructor */
    Tracker(ParentType* p): parent(p)
    { /* skip */}

    /* Destructor */
    virtual ~Tracker()
    { /* skip */ }
  };

  /* Base class for all LLVM type representations. */
  class TypeRepr: public SimpleList<TypeRepr>::Node
  {
  protected:
    // Object tracker
    Tracker<TypeRepr> tracker_;

  public:
    /* Types */

    // Possible kinds of LLVM type.
    enum TypeKind
      {
        ATOMIC,
        POINTER,
        ARRAY,
        VECTOR,
        STRUCT,
        FUNC,
        FIELD,
        UNDEF
      };

  public:
    /* Fields */
      
    // The kind of LLVM type.
    const TypeKind kind_;

    // The type being represented.
    const llvm::Type* llvm_type_;

    // The representation of the type.
    tree repr_;

    /* Constructor */
    explicit TypeRepr(TypeKind k,
                      const llvm::Type* lv = 0,
                      tree r = NULL_TREE)
      : tracker_(this), kind_(k), llvm_type_(lv), repr_(r)
    { }

  public:
    /* Public constructor */
    explicit TypeRepr(const llvm::Type* lv = 0,
                      tree r = NULL_TREE)
      : tracker_(this), kind_(ATOMIC), llvm_type_(lv), repr_(r)
    {}

    /* Destructor */
    virtual ~TypeRepr()
    {
      this->llvm_type_ = 0;
      this->repr_ = 0;
    }

    /* Methods */
    Tracker<TypeRepr>* get_tracker()
    {
      return &(this->tracker_);
    }

    TypeKind get_kind()
    {
      return this->kind_;
    }

    const llvm::Type* get_type()
    {
      return this->llvm_type_;
    }

    void set_type(const llvm::Type* lv)
    {
      this->llvm_type_ = lv;
    }

    tree get_tree()
    {
      return this->repr_;
    }

    void set_tree(tree t)
    {
      this->repr_ = t;
    }
  };
  /* Utility types */
  typedef Tracker<TypeRepr> TypeReprTracker;
  typedef SimpleList< TypeReprTracker > TypeReprTrackerList;

  /* Containers for types */
  typedef SimpleList<TypeRepr> TypeReprSimpleList;
  typedef std::map<const llvm::Type*, TypeRepr*> TypeReprMap;
  typedef std::vector<TypeRepr*> TypeReprSeq;

  /* Information for components of a type.  Used to add a GENERIC
     expression around a type embedded in another.  */
  class FieldRepr: public TypeRepr
  {
  public:
    /* Fields */

    // The type being wrapped.
    TypeRepr* subtype;

    // The field declaration;
    tree decln;

    /* Constructor */
    explicit FieldRepr(TypeRepr* s = 0,
                       tree fd = NULL_TREE)
      : TypeRepr(FIELD, 0, NULL_TREE), subtype(s), decln(fd)
    {}

    /* Get the tree from the subtype (in llvmir.cc) */
    virtual tree get_tree();

    /* Get the type from the subtype (in llvmir.cc) */
    virtual const llvm::Type* get_type();
  };
  typedef std::vector<FieldRepr*> FieldReprSeq;

  /* LLVM Pointer types. */
  class PtrTypeRepr: public TypeRepr
  {
  public:
    /* Fields */

    // The base type.
    TypeRepr* base_type;

    /* Constructor */
    explicit PtrTypeRepr(const llvm::PointerType* lt = 0,
                         tree r = NULL_TREE)
      : TypeRepr(POINTER, lt, r), base_type(0)
    {}

    /* Methods */
    const llvm::PointerType* get_ptr_type()
    {
      return static_cast<const llvm::PointerType*>(llvm_type_);
    }
  };

  /* LLVM Struct types. */
  class StructTypeRepr: public TypeRepr
  {
  public:
    /* Fields */

    // The structure fields. These will be trees of type FIELD_DECL.
    // The object at fields[i] is the representation for each subtype at index i
    // in the LLVM type.
    FieldReprSeq fields;

    /* Constructor */
    explicit StructTypeRepr(const llvm::StructType* lt = 0,
                            tree r = NULL_TREE)
      : TypeRepr(STRUCT, lt, r)
    {
      // Reserve some space for the subtypes when possible.
      if (lt)
        fields.reserve(lt->getNumElements());
    }

    /* Destructor */
    ~StructTypeRepr()
    { }
  };

  /* Array types. */
  class ArrayTypeRepr: public TypeRepr
  {
  public:
    /* Fields. */

    // Number of elements.
    size_t num_elems;

    // Element type.
    TypeRepr* elem_type;

    /* Constructor */
    explicit ArrayTypeRepr(const llvm::ArrayType* lt = 0,
                           tree r = NULL_TREE,
                           size_t n = 0,
                           TypeRepr* et = 0)
      : TypeRepr(ARRAY, lt, r), num_elems(n), elem_type(et)
    { }

    /* Destructor */
    ~ArrayTypeRepr()
    { }
  };

  /* Vector types. */
  class VectorTypeRepr: public TypeRepr
  {
  public:
    /* Fields. */

    // Number of elements.
    size_t num_elems;

    // Element type.
    TypeRepr* elem_type;

    /* Constructor */
    explicit VectorTypeRepr(const llvm::VectorType* lt = 0,
                            tree r = NULL_TREE,
                            size_t n = 0,
                            TypeRepr* et = 0)
      : TypeRepr(VECTOR, lt, r), num_elems(n), elem_type(et)
    { }

    /* Destructor */
    ~VectorTypeRepr()
    { }
  };

  /* LLVM Function types. */
  class FuncTypeRepr: public TypeRepr
  {
  public:
    /* Constructor */
    explicit FuncTypeRepr(const llvm::FunctionType* lt = 0,
                          tree r = NULL_TREE)
      : TypeRepr(FUNC, lt, r)
    {}

    /* Methods */
    const llvm::FunctionType* get_fn_type()
    {
      return static_cast<const llvm::FunctionType*>(llvm_type_);
    }

    const llvm::Type* get_return_type()
    {
      const llvm::FunctionType* llvm_fn_type = this->get_fn_type();

      if (llvm_fn_type)
        return llvm_fn_type->getReturnType();

      return 0;
    }

    // Get the tree for the function return type.
    tree get_return_tree()
    {
      tree fn_tree = this->get_tree();
      if (fn_tree == NULL_TREE)
        return NULL_TREE;

      gcc_assert(FUNC_OR_METHOD_CHECK(fn_tree));      

      return TREE_TYPE(fn_tree);
    }

    // Get the tree for the argument types.
    tree get_arg_types_tree()
    {
      tree fn_tree = this->get_tree();
      if (fn_tree == NULL_TREE)
        return NULL_TREE;

      gcc_assert(FUNC_OR_METHOD_CHECK(fn_tree));      

      return TYPE_ARG_TYPES(fn_tree);
    }
  };

  /* Base class for all LLVM value representations. */
  class ValueRepr: public SimpleList<ValueRepr>::Node
  {
  protected:
    // Object tracker
    Tracker<ValueRepr> tracker_;

  public:
    /* Types */

    // Possible kinds of LLVM value.
    enum ValueKind
      {
        VALUE,
        INSTR,
        BLOCK,
        VARIABLE,
        FUNCTION,
        UNDEF
      };

  protected:
    /* Fields */
      
    // The kind of LLVM value.
    const ValueKind kind_;

    // The value being represented.
    const llvm::Value* llvm_value_;

    // Whether the llvm value is managed by this representation.
    bool llvm_value_managed_;

    // The representation of the value.
    tree repr_;

    /* Constructor */
    explicit ValueRepr(ValueKind k,
                       const llvm::Value* lv,
                       tree r)
      : tracker_(this), kind_(k),
      llvm_value_(lv), llvm_value_managed_(false), repr_(r)
    {}

  public:
    /* Public constructor */
    explicit ValueRepr(const llvm::Value* lv = 0,
                       tree r = NULL_TREE)
      : tracker_(this), kind_(VALUE), 
      llvm_value_(lv), llvm_value_managed_(false),
      repr_(r)
    {}

    /* Destructor (in llvmir.cc) */
    virtual ~ValueRepr();

    /* Methods */
    Tracker<ValueRepr>* get_tracker()
    {
      return &(this->tracker_);
    }

    ValueKind get_kind()
    {
      return this->kind_;
    }

    const llvm::Value* get_value()
    {
      return this->llvm_value_;
    }

    void set_value(const llvm::Value* lv)
    {
      this->llvm_value_ = lv;
    }

    tree get_tree()
    {
      return this->repr_;
    }

    void set_tree(tree t)
    {
      this->repr_ = t;
    }

    void set_managed(bool b)
    {
      llvm_value_managed_ = b;
    }
  };
  /* Utility types */
  typedef Tracker<ValueRepr> ValueReprTracker;
  typedef SimpleList< ValueReprTracker > ValueReprTrackerList;

  /* Containers for values */
  typedef SimpleList<ValueRepr> ValueReprSimpleList;
  typedef std::list<ValueRepr*> ValueReprList;
  typedef std::vector<ValueRepr*> ValueReprSeq;
  typedef std::map<const llvm::Value*, ValueRepr*> ValueReprMap;

  /* An LLVM instruction is represented as a tree, giving the result
     of the instruction, and the list of GENERIC instructions needed to
     generate that tree. */
  class InstrRepr: public ValueRepr
  {
  protected:
    // The instructions to generate the value. Set to NULL_TREE
    // if the LLVM instruction has not yet been translated.
    tree instrs_;

    // Extra instructions to add after instrs.
    tree extra_instrs_;

  public:
    /* Fields */

    // Whether this represents a block terminator.
    bool is_terminator;

    /* Constructor. For InstrRepr(i, r, il), i is the LLVM
       instruction, r is the generated value and il is the list of
       instructions which generate r. */
    explicit InstrRepr(const llvm::Instruction* i,
                       tree r, tree il,
                       bool t = false)
      : ValueRepr(INSTR, i, r),
      instrs_(il), extra_instrs_(NULL_TREE), is_terminator(t)
    {}

    /* Destructor (in llvmir.cc); */
    ~InstrRepr();

    /* Methods */
    const llvm::Instruction* get_instr()
    {
      return static_cast<const llvm::Instruction*>(llvm_value_);
    }

    // Add instructions to the end of the list.
    virtual void add_instrs(tree il)
    {
      append_to_statement_list(il, &instrs_);
    }

    // Get the instruction list.
    virtual tree get_instrs()
    {
      return instrs_;
    }

    // Add instructions to be executed after instrs.
    virtual void add_extra_instrs(tree extra)
    {
      append_to_statement_list(extra, &extra_instrs_);
    }

    // Build the TREE statements for this instruction, incorporating
    // the extra statements to be added at the end of the instruction.
    // This is a virtual function to allow TerminalInstrRepr to be
    // passed extra data before building the final tree.
    virtual tree build()
    {
      tree stmt_list = instrs_;
      append_to_statement_list(extra_instrs_, &stmt_list);
      return stmt_list;
    }

    // Copy the contents of another InstrRepr
    void copy(const InstrRepr* other)
    {
      if (!other)
        return;
	
      this->llvm_value_ = other->llvm_value_;
      this->repr_ = other->repr_;
      this->instrs_ = other->instrs_;
    }

    // Replace this instruction with another
    void replace(InstrRepr* other)
    {
      if (!other)
        return;
	
      this->copy(other);

      // Replace in any list.
      if (this->list())
        this->list()->replace(this, other);
    }

    // Test for having been translated.
    bool is_converted()
    {
      return this->instrs_ != NULL_TREE;
    }

  };
  /* List of instructions */
  typedef SimpleList<ValueRepr> InstrReprList;

  /* Invoke instructions have to embed any extra code into the returned tree. */
  class InvokeInstrRepr: public InstrRepr
  {
  public:
    /* Fields */

    // Location.
    location_t loc;

    // Type of the expression.
    tree expr_type;

    // Call statement
    tree call_stmt;

    // Goto normal label
    tree goto_normal;

    // Goto exception label
    tree goto_exception;

    /* Constructor */
    explicit InvokeInstrRepr(const llvm::Instruction* i = 0,
                             tree r = NULL_TREE,
                             tree il = NULL_TREE)
      : InstrRepr(i, r, il, true)
    {}

    // Code builder (defined in convert_functions.cc).
    virtual tree build();
  };

  /* Block terminator instructions have accept additional code, to be
     inserted at the end of a block.*/
  class TerminalInstrRepr: public InstrRepr
  {
  public:
    /* Fields */

    /* Constructor */
    explicit TerminalInstrRepr(const llvm::Instruction* i = 0,
                               tree r = NULL_TREE,
                               tree il = NULL_TREE)
      : InstrRepr(i, r, il, true)
    {}

    /* Methods */

    // For terminator instructions which do not return values, extra
    // code is inserted before the code for the instruction
    // (in convert_functions.cc).
    virtual tree build();
  };

  /* An LLVM basic block is represented as a label and the list of
     instructions contained in the block. */
  class BlockRepr: public ValueRepr
  {
  protected:
    /* Fields */

    // Label to use when refering to the tree.
    ValueRepr* label_;

    // Whether the instructions have been converted.
    bool is_converted_;

  public:
    /* Fields */

    // The instruction terminating the block. Can be 0.
    InstrRepr* terminator;

    // Instructions in the block. Empty if the LLVM block has not been
    // converted otherwise contains at least one instruction (either a
    // label or a nop).
    InstrReprList instrs;

    // Code to add at the end of the list, before the block
    // terminator.
    InstrReprList extra_end_instrs;

    /* Constructor */
    explicit BlockRepr(const llvm::BasicBlock* lb = 0,
                       tree l = NULL_TREE)
      : ValueRepr(BLOCK, lb, l),
        label_(0), is_converted_(false), terminator(0)
    {/* skip */}

    /* Destructor (in llvmir.cc) */
    virtual ~BlockRepr();

    /* Methods */
    const llvm::BasicBlock* get_block()
    {
      return static_cast<const llvm::BasicBlock*>(this->llvm_value_);
    }

    ValueRepr* get_label()
    {
      return this->label_;
    }

    void set_label(ValueRepr* t)
    {
      this->label_ = t;
      this->set_tree(t->get_tree());
    }

    // Test for having been converted.
    bool is_converted()
    {
      return is_converted_;
    }
    
    // Mark as converted.
    void set_converted(bool v)
    {
      is_converted_ = v;
    }
  };
  /* List of blocks */
  typedef std::list<BlockRepr*> BlockReprStdList;


  /* An LLVM global variable declaration or definition. */
  class VarRepr: public ValueRepr
  {
  public:
    /* Fields */

    ////
    // Variable properties

    // Whether the variable is a constant.
    bool is_constant;

    // The type.
    TypeRepr *var_type;

    // The declaration (get_tree() is the address of the declaration).
    tree decln_tree;

    // The definition (0 if the variable is a declaration).
    ValueRepr* defn_repr;

    /* Constructor */
    explicit VarRepr(const llvm::GlobalVariable* lv = 0,
                     tree d = NULL_TREE)
      : ValueRepr(VARIABLE, lv, d),
        is_constant(false), var_type(0),
        decln_tree(NULL_TREE), defn_repr(0)
    {}

    /* Destructor */
    virtual ~VarRepr();

    /* Methods */
    const llvm::GlobalVariable* get_variable()
    {
      return static_cast<const llvm::GlobalVariable*>(this->llvm_value_);
    }
    
    // Get the variable declaration.
    tree get_decln()
    {
      return decln_tree;
    }

    // Test for declaration only.
    bool is_declaration()
    {
      if (this->llvm_value_
          && this->get_variable()->isDeclaration())
        return true;

      return false;
    }
  };
  /* List of variables */
  typedef ValueReprList VarReprList;

  /* An LLVM Function declaration or definition. The tree is the
     FNDECL of the function.
  */
  class FunctionRepr: public ValueRepr
  {
  public:
    /* Fields */

    ////
    // Function properties

    // Whether the function is variadic.
    bool variadic;

    // Function type.
    FuncTypeRepr *fn_type;

    // Function declaration.
    tree decln_tree;

    // Function scope. All local variables belong in this block.
    tree scope_tree;

    // The function result. 0 if the function has no return.
    ValueRepr* result;

    // Function body. This is a convenience pointer into the FNDECL tree.
    tree stmt_list;

    /* Constructor */
    explicit FunctionRepr(const llvm::Function* lf = 0,
                          tree d = NULL_TREE)
      : ValueRepr(FUNCTION, lf, d),
        variadic(false), fn_type(0), decln_tree(NULL_TREE), 
        scope_tree(NULL_TREE), result(0)
    {}

    /* Destructor */
    virtual ~FunctionRepr();

    /* Methods */
    const llvm::Function* get_function()
    {
      return static_cast<const llvm::Function*>(this->llvm_value_);
    }
    
    // Get the function declaration.
    tree get_decln()
    {
      return decln_tree;
    }

    // Test for declaration only.
    bool is_declaration()
    {
      if (this->llvm_value_
          && this->get_function()->isDeclaration())
        return true;
      return false;
    }
  };
  /* List of functions */
  typedef ValueReprList FunctionReprList;

} // namespace llvmir

/* End of file. */
#endif /* REPR_H */
